#! /usr/bin/env python

##
##    This program is free software; you can redistribute it and/or modify
##    it under the terms of the GNU General Public License as published by
##    the Free Software Foundation; either version 2 of the License, or
##     ( at your option )  any later version.
##
##    This program is distributed in the hope that it will be useful,
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##    GNU General Public License for more details.
##
##    You should have received a copy of the GNU General Public License
##    along with this program; if not, write to the Free Software
##    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
##

import pygtk
pygtk.require('2.0')
import gtk, gobject, cairo, math, rsvg
from gtk import gdk

# Create a GTK+ widget on which we will draw using Cairo
class Screen(gtk.DrawingArea):

    # Draw in response to an expose-event
    __gsignals__ = { "expose-event": "override" }

    def __init__(self):
        super(Screen,self).__init__()
        # gtk.Widget signals
        self.connect("button_press_event", self.button_press)
        self.connect("button_release_event", self.button_release)
        self.connect("motion_notify_event", self.motion_notify)
        self.connect("configure_event", self.configure) #resize
        self.connect("realize", self.realize)
        # More GTK voodoo : unmask events
        self.add_events(gdk.BUTTON_PRESS_MASK |
                        gdk.BUTTON_RELEASE_MASK |
                        gdk.POINTER_MOTION_MASK)
        self.cr = None

    # Handle the expose-event by drawing
    def do_expose_event(self, event):

        # Create the cairo context
        self.cr = self.window.cairo_create()
        self.hitpath = None #Is set later

        # Restrict Cairo to the exposed area; avoid extra work
        self.cr.rectangle(event.area.x, event.area.y,
                event.area.width, event.area.height)
        self.cr.clip()

        self.draw(self.cr, *self.window.get_size())


    def draw(self, cr, width, height):
	    pass

    def button_press(self,widget,event):
        pass
    def button_release(self,widget,event):
        pass
    def motion_notify(self,widget,event):
        pass
    def configure(self,widget,event):
        pass
    def realize(self,widget):
        pass  

# Create a GTK+ widget on which we will draw using Cairo
class AlphaScreen(gtk.EventBox):

    # Draw in response to an expose-event
    __gsignals__ = { "expose-event": "override" }

    def __init__(self):
        super(AlphaScreen,self).__init__()
        self.set_flags (gtk.NO_WINDOW)
        # gtk.Widget signals
        self.connect("button_press_event", self.button_press)
        self.connect("button_release_event", self.button_release)
        self.connect("motion_notify_event", self.motion_notify)
        self.connect("configure_event", self.configure) #resize
        self.connect("realize", self.realize)
        # More GTK voodoo : unmask events
        self.add_events(gdk.BUTTON_PRESS_MASK |
                        gdk.BUTTON_RELEASE_MASK |
                        gdk.POINTER_MOTION_MASK)
        self.cr = None
        self.supports_alpha = False
        self.set_app_paintable(True)
        self.connect('screen-changed', self.screen_changed)
        #self.screen_changed(self)
        self.posx,self.posy = 0,0
        
        
    def set_position(self,x,y):
        self.posx, self.posy = x,y
        self.queue_draw()

    # Handle the expose-event by drawing
    def do_expose_event(self, event):

        # Create the cairo context
        self.cr = self.window.cairo_create()
        self.hitpath = None #Is set later

        # Restrict Cairo to the exposed area; avoid extra work
        self.cr.rectangle(event.area.x, event.area.y,
                event.area.width, event.area.height)
        self.cr.clip()

        self.draw(self.cr, *self.window.get_size())

    #If screen changes, it could be possible we no longer got rgba colors
    def screen_changed(self,widget, old_screen=None):
        screen = widget.get_screen()
        colormap = screen.get_rgba_colormap()
        if colormap == None:
            print 'Your screen does not support alpha channels!'
            colormap = screen.get_rgb_colormap()
            self.supports_alpha = False
        else:
            print 'Your screen supports alpha channels!'
            self.supports_alpha = True
        
        widget.set_colormap(colormap)
        
        return True

    def draw(self, cr, width, height):
	    pass

    def button_press(self,widget,event):
        pass
    def button_release(self,widget,event):
        pass
    def motion_notify(self,widget,event):
        pass
    def configure(self,widget,event):
        pass
    def realize(self,widget):
        pass        

# GTK mumbo-jumbo to show the widget in a window and quit when it's closed
def run(Widget):
    window = TransparentWindow()#gtk.Window()
    
    #window.set_composited(True)
    #window.connect("delete-event", gtk.main_quit)
    #window.connect("expose-event",transparent_expose)
    widget = Widget()
    
    #widget.screen_changed(window)
    widget.show()
    window.add(widget)
    window.present()
    
    #widget.window.set_composited(True)
    gtk.main()



class TransparentWindow(gtk.Window):
    
    __gsignals__ = { "expose-event": "override" ,
                    "screen-changed": "override" }
    
    def __init__(self):
        #gtk.Window.__init__(self)
        super(TransparentWindow,self).__init__()
        self.connect("delete-event", gtk.main_quit)
        self.connect('destroy', self.destroy)
        #self.set_background_svg("player.svg")
        self.supports_alpha = False
        self.do_screen_changed(self)
        self.bgsvg = None

    def do_expose_event(self, event):

        cr = self.window.cairo_create()
        cr.set_operator(cairo.OPERATOR_CLEAR)
        # Ugly but we don't have event.region
        region = gtk.gdk.region_rectangle(event.area)
        cr.region(region)
        cr.fill()
        #
        if self.supports_alpha:
            cr.set_source_rgba(1.0, 1.0, 1.0, 0.0)
        else:
            cr.set_source_rgb(1.0, 1.0, 1.0)
        cr.set_operator(cairo.OPERATOR_SOURCE)
        cr.paint()

        cr.set_operator(cairo.OPERATOR_OVER)

        if self.bgsvg:
            self.bgsvg.render_cairo(cr)
            

        children = self.get_children()
        for c in children:
            self.propagate_expose(c, event)
            #children2 = c.get_children()
            #for c2 in children2:
            #    c2.send_expose( event)
            #    print c2

        #print "Trans"
        return False
        
    def set_background_svg(self, fn):
        self.bgsvg = rsvg.Handle(file=fn)
        self.queue_draw()
        
    def destroy(self,e):
        if self.bgsvg:
            self.bgsvg.close()
            
    def do_screen_changed(self,widget, old_screen=None):
        screen = widget.get_screen()
        colormap = screen.get_rgba_colormap()
        if colormap == None:
            #print 'Your screen does not support alpha channels!'
            colormap = screen.get_rgb_colormap()
            self.supports_alpha = False
        else:
            #print 'Your screen supports alpha channels!'
            self.supports_alpha = True
            
        widget.set_colormap(colormap)
        
        return True
    

class CairoHScroll(AlphaScreen):

    def __init__(self):
        AlphaScreen.__init__(self)
        #gtk.gdk.flush()
        
        self.x = self.y = 10
        self.fx,self.fy = 0,0 # 'final' values used to calculate scroll value
        self.fw,self.fh = 0,0
        #size
        self.sx = 20
        self.sy = 25
        self.padding = 5
        
        self.oxx = self.oyy = self.ox = self.oy = 0
        self.drag = False
        self.hit = False

        self.range = (0.0,1.0)
        self.value_range = self.value = 0.0
        
    def realize(self,widget):
        self.window.set_composited(True)
        #self.window.set_app_paintable(True)

    def set_range(self,a,b):
        self.range = (a,b)
    
    def get_range(self):
        return self.range
    
    def get_value(self):
        self.calc_value()
        return self.value_range
        
    def calc_value(self):
        self.value = float(self.fx)/ (self.fw - self.padding)
        self.value_range = self.value * (self.range[1] - self.range[0]) + self.range[0]
        
    #Override the press event
    def button_press(self,widget,event):
        self.drag = True
        if self.hitpath:
            self.cr.append_path(self.hitpath) # re-gen the path
            self.hit = self.cr.in_fill(event.x, event.y) # Test it. Sweet.
        #self.oxx = event.x - self.ox
        #self.oyy = event.y - self.oy
        self.x,self.y = event.x,event.y
        self.queue_draw()
        
    def button_release(self,widget,event):
        self.drag = False
        
    def configure(self,widget,event):
        w,h = self.window.get_size()
        self.x = self.value * w

    def draw(self, cr, width, height):
        w,h = self.window.get_size()
        
        ww = w - w % (self.sx + self.padding)
        x,y = self.x,0 # self.y - self.oyy for vertical
        
        if x >= ww - self.padding:
            x = ww - self.padding #Ignore the last padding
        if x < 0:
            x = 0

        self.fx = x
        self.fy = y
        self.fw = ww
        self.fh = h
        self.calc_value()
        
       #Make the window transparent
        if self.supports_alpha == True:
            cr.set_source_rgba(1.0, 1.0, 1.0, 0.0)
        else:
            cr.set_source_rgb(1.0, 1.0, 1.0) 
        

        #cr.set_operator(cairo.OPERATOR_SOURCE)
        #cr.paint()
        
        cr.set_operator(cairo.OPERATOR_OVER)
        
        #set clipping        
        #cr.transform(cairo.Matrix(1, 0, 0, 1, self.sy*2, 0))
        #cr.rotate(math.pi/2)
        cr.curve_to(0,self.sy, w/2,self.sy/1.5, w,self.sy*2)
        cr.line_to(w,0)
        cr.line_to(0,0)
        cr.close_path()
        #if we want to do hit test, but we use the hole widget so no need to
        #self.hitpath = cr.copy_path_flat() #record the path to use as a hit area.
        
        
        cr.clip()
        
        #background blocks
        cr.set_source_rgba(0,0,1,0.2)
        for i in range(0,ww/25):
            cr.rectangle(i*25,0,20,50)
        cr.clip()
        
        #Allow gradient use etc.
        cr.rectangle(0,0,ww,h)
        cr.fill()
        
        #foreground blocks
        cr.new_path()
        cr.set_source_rgba(0,0,1,.8)
        last = int(math.ceil(ww * self.value) / (self.sx +self.padding))
        for i in range(0, last ):
            cr.rectangle(i * (self.sx+self.padding) ,0,self.sx,self.sy*2)
        
        cr.rectangle(last ,0, (ww * self.value) - last ,self.sy*2)
        cr.clip()
        
        #Allows gradient etc
        linear = cairo.LinearGradient(0, 0, ww * self.value, 0)
        linear.add_color_stop_rgba(0.0,  0, 0, 0, 0.5)
        linear.add_color_stop_rgba(.5,  0, 0, 1, .75)
        linear.add_color_stop_rgba(1,  1, 1, 1, 1)
        cr.set_source(linear)
        cr.rectangle(0,0,ww*self.value,h)
        cr.fill()
        
        '''
        cr.new_path()
        cr.move_to(x,y)
        cr.line_to(x+sx,y)
        cr.line_to(x+sx,y+sy*2)
        cr.line_to(x,y+sy*2)

        cr.close_path()
        cr.set_source_rgba(1,0,0,1)
        '''

        #self.hitpath = cr.copy_path_flat() #record the path to use as a hit area.
        #self.ox,self.oy = cr.get_current_point() # should get most left point of the knob here
        #print self.ox,self.oy
        #cr.fill() #consumes the path, so get it before the fill
        
        return False
        
    def motion_notify(self,widget,event):
        if self.drag and self.hit:
            self.x, self.y = event.x, event.y
            self.queue_draw()


  
class CairoHScroll2(CairoHScroll):
    def __init__(self):
        super(CairoHScroll2,self).__init__()
        self.svg_track = rsvg.Handle(file="scrollbar.svg")
        self.svg_knob = rsvg.Handle(file="scrollbar-knob.svg")  
        self.padding = 0
        
    def draw(self, cr, width, height):
        w,h = self.svg_knob.props.width ,self.svg_knob.props.height  #self.window.get_size()
        x,y = self.x - 2.5 - self.posx ,0 #- self.posy # self.y - self.oyy for vertical
        ax,ay,aw,ah = self.get_allocation()
        
        if x >= w:
            x = w
        if x < 0:
            x = 0
        
        self.fx = x 
        self.fy = y 
        self.fw = w
        self.fh = h
        self.calc_value()
        
       #Make the window transparent
        #if self.supports_alpha == True:
        #    cr.set_source_rgba(1.0, 1.0, 1.0, 0.0)
        #else:
        #    cr.set_source_rgb(1.0, 1.0, 1.0) 
        #cr.set_operator(cairo.OPERATOR_SOURCE)
        #cr.paint()
        
        cr.set_operator(cairo.OPERATOR_OVER)
        cr.transform(cairo.Matrix(1,0,0,1,ax + self.posx,ay + self.posy))
        self.svg_track.render_cairo(cr)
        cr.rectangle(ax,ay,self.fw,self.fh)
        self.hitpath = cr.copy_path_flat()
        
        cr.new_path()
        if self.value > 0:
            cr.transform(cairo.Matrix(self.value,0,0,1,2.5,3))
            self.svg_knob.render_cairo(cr)


class CairoButton(AlphaScreen):
    def __init__(self,fn):
        super(CairoButton,self).__init__()
        self.svg_button = rsvg.Handle(file=fn)
        self.set_size_request(24,24)
        
    def draw(self, cr, width, height):
        #w,h = self.svg_button.props.width ,self.svg_button.props.height  #self.window.get_size()
        #x,y = self.x - 2.5 - self.posx ,0 #- self.posy # self.y - self.oyy for vertical
        ax,ay,aw,ah = self.get_allocation()
        print ax,ay,aw,ah
        
        cr.set_operator(cairo.OPERATOR_OVER)
        cr.transform(cairo.Matrix(1,0,0,1,ax + self.posx,ay + self.posy))
        self.svg_button.render_cairo(cr)

class CairoLabel(AlphaScreen):
    
    def __init__(self, label = None):
        AlphaScreen.__init__(self)
        if not label:
            self.label = 'See on ka yks pikk tekst'
        else:
            self.label = label
        self.font_color = None
        self.set_color(1,0,.5,1)
        self.font_name = "Sans"
        self.font_size = 34
        self.font_weight = cairo.FONT_WEIGHT_NORMAL
        

    def set_label(self,label):
        self.label = label
        
    def set_color(self,r=1.0,g=1.0,b=1.0,a = 1.0):
        self.font_color = (r,g,b,a)
        
    def draw(self,cr,height,width):
        self.w,self.h = self.window.get_size()
        ax,ay,aw,ah = self.get_allocation()
        print ax,ay,aw,ah
        
        if self.supports_alpha:
            cr.set_source_rgba(1.0, 1.0, 1.0, 0.0)
        else:
            cr.set_source_rgb(1.0, 1.0, 1.0)
        #cr.set_operator(cairo.OPERATOR_SOURCE)
        #cr.paint()
        
        cr.new_path()
        cr.set_source_rgba(self.font_color[0], self.font_color[1], self.font_color[2], self.font_color[3])
        cr.select_font_face(self.font_name,cairo.FONT_SLANT_NORMAL, self.font_weight)
        cr.set_font_size(self.font_size)
        self.x_bearing, self.y_bearing, self.twidth, self.theight = cr.text_extents(self.label)[:4]
        #cr.move_to( self.twidth / 2 - self.x_bearing,  self.theight / 2 - self.y_bearing)
        cr.move_to(ax + self.posx, ay + self.posy + (self.theight / 2 - self.y_bearing))
        cr.text_path(self.label)
        #self.warpPath(cr,self.halfcircle)
        cr.fill()
        #cr.show_text(self.label)
            
    def warpPath(self,ctx, function):
        first = True

        for type, points in ctx.copy_path():
            if type == cairo.PATH_MOVE_TO:
                if first:
                    ctx.new_path()
                    first = False
                x, y = function(*points)
                ctx.move_to(x, y)

            elif type == cairo.PATH_LINE_TO:
                x, y = function(*points)
                ctx.line_to(x, y)

            elif type == cairo.PATH_CURVE_TO:
                x1, y1, x2, y2, x3, y3 = points
                x1, y1 = function(x1, y1)
                x2, y2 = function(x2, y2)
                x3, y3 = function(x3, y3)
                ctx.curve_to(x1, y1, x2, y2, x3, y3)

            elif type == cairo.PATH_CLOSE_PATH:
                ctx.close_path()

    def spiral(self,x, y):
        theta0 = -math.pi * 3 / 4
        theta = x / self.twidth * math.pi * 2 + theta0
        radius = y + 200 - x/7
        xnew = radius*math.cos(theta)
        ynew = radius*math.sin(-theta)
        return xnew + self.w/2, ynew + self.h/2

    def curl(self,x, y):
        xn = x - self.twidth/2
        #yn = y - Textheight/2
        xnew = xn
        ynew = y + xn ** 3 / ((self.twidth/2)**3) * 70
        return xnew + self.w/2, ynew + self.h*2/5
        
    def circle(self,x, y):
        theta = x / (self.twidth + 10)* math.pi * 2 + math.pi 
        radius = self.twidth / (2*math.pi)  - y
        xnew = radius*math.cos(theta) 
        ynew = radius*math.sin(theta) 
        return xnew + self.w/2, ynew + self.h/2
        
    def halfcircle(self,x, y):
        theta = (x / (self.twidth + 10)* math.pi )+ math.pi
        radius = self.twidth / (2*math.pi)  - y
        xnew = radius*math.cos(theta) 
        ynew = radius*math.sin(theta) 
        return xnew + self.w/2, ynew + self.h/2
        
class WidgetContainer(gtk.Container):
    def __init__(self):
        #super(WidgetContainer,self).__init__()
        pass
        
        
if __name__=="__main__":
    run(CairoLabel)

